package com.ohos.connent.bizscenecloud.dev.modular.file.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;

import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.*;
import com.ohos.connent.bizscenecloud.common.exception.CommonException;
import com.ohos.connent.bizscenecloud.dev.api.DevConfigApi;
import com.ohos.connent.bizscenecloud.dev.modular.file.enums.DevFileBucketAuthEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.List;

/**
 * 华为云文件工具类
 * 参考文档：https://support.huaweicloud.com/usermanual-obs/zh-cn_topic_0045829088.html
 * https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0100.html
 *
 *
 */
@Slf4j
public class DevFileHuaWeiyunUtil {

    private static ObsClient obsClient;

    private static String defaultBucketName;
    private static String endPoint;

    private static final String OHOSCON_FILE_HWYUN_ACCESS_KEY_ID_KEY = "OHOSCON_FILE_HWYUN_ACCESS_KEY_ID";
    private static final String OHOSCON_FILE_HWYUN_ACCESS_KEY_SECRET_KEY = "OHOSCON_FILE_HWYUN_ACCESS_KEY_SECRET";
    private static final String OHOSCON_FILE_HWYUN_END_POINT_KEY = "OHOSCON_FILE_HWYUN_END_POINT";
    private static final String OHOSCON_FILE_HWYUN_DEFAULT_BUCKET_NAME = "OHOSCON_FILE_HWYUN_DEFAULT_BUCKET_NAME";

    /**
     * 初始化操作的客户端
     *
     *
     */
    private static void initClient() {

        DevConfigApi devConfigApi = SpringUtil.getBean(DevConfigApi.class);

        /* accessKeyId */
        String accessKeyId = devConfigApi.getValueByKey(OHOSCON_FILE_HWYUN_ACCESS_KEY_ID_KEY);

        if(ObjectUtil.isEmpty(accessKeyId)) {
            throw new CommonException("华为云文件操作客户端未正确配置：accessKeyId为空");
        }

        /* accessKeySecret */
        String accessKeySecret = devConfigApi.getValueByKey(OHOSCON_FILE_HWYUN_ACCESS_KEY_SECRET_KEY);

        if(ObjectUtil.isEmpty(accessKeySecret)) {
            throw new CommonException("华为云文件操作客户端未正确配置：accessKeySecret为空");
        }

        /* endpoint */
        endPoint = devConfigApi.getValueByKey(OHOSCON_FILE_HWYUN_END_POINT_KEY);

        if(ObjectUtil.isEmpty(endPoint)) {
            throw new CommonException("华为云文件操作客户端未正确配置：endpoint为空");
        }

        /* 默认BucketName */
        defaultBucketName = devConfigApi.getValueByKey(OHOSCON_FILE_HWYUN_DEFAULT_BUCKET_NAME);

        if(ObjectUtil.isEmpty(defaultBucketName)) {
            throw new CommonException("华为云文件操作客户端未正确配置：defaultBucketName为空");
        }

        obsClient = new ObsClient(accessKeyId, accessKeySecret,endPoint);
    }

    /**
     * 获取默认存储桶名称
     *
     *
     **/
    public static String getDefaultBucketName() {
        initClient();
        return defaultBucketName;
    }

    public static String getEndPoint() {
        initClient();
        return endPoint;
    }

    /**
     * 销毁操作的客户端
     *
     *
     */
    public static void destroyClient() {
        initClient();
        try {
            obsClient.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取操作的客户端
     *
     *
     */
    public ObsClient getClient() {
        initClient();
        return obsClient;
    }

    /**
     * 查询存储桶是否存在
     * 例如：传入参数examplebucket-1250000000，返回true代表存在此桶
     *
     * @param bucketName 桶名称
     *
     */
    public boolean headBucket(String bucketName) {
        try {
            initClient();
            return  obsClient.headBucket( bucketName);
        } catch (ObsException e) {
            throw new CommonException(e.getMessage());
        }
    }

    /**
     * 设置预定义策略
     * 预定义策略如公有读、公有读写、私有读
     *
     * @param bucketName 桶名称
     * @param devFileBucketAuthEnum 存储桶权限
     *
     */
    public static void setBucketAcl(String bucketName, DevFileBucketAuthEnum devFileBucketAuthEnum) {
        try {
            initClient();
            if (devFileBucketAuthEnum.equals(DevFileBucketAuthEnum.PRIVATE)) {
                obsClient.setBucketAcl(bucketName, AccessControlList.REST_CANNED_PRIVATE);
            } else if (devFileBucketAuthEnum.equals(DevFileBucketAuthEnum.PUBLIC_READ)) {
                obsClient.setBucketAcl(bucketName, AccessControlList.REST_CANNED_PUBLIC_READ);
            } else if (devFileBucketAuthEnum.equals(DevFileBucketAuthEnum.PUBLIC_READ_WRITE)) {
                obsClient.setBucketAcl(bucketName, AccessControlList.REST_CANNED_PUBLIC_READ_WRITE);
            }
        } catch (ObsException e) {
            throw new CommonException(e.getMessage());
        }
    }

    /**
     * 判断是否存在文件
     *
     * @param bucketName 桶名称
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     *
     */
    public static boolean isExistingFile(String bucketName, String key) {
        try {
            initClient();
            return obsClient.doesObjectExist(bucketName, key);
        } catch (ObsException e) {
            throw new CommonException(e.getMessage());
        }
    }

    /**
     * 存储文件，不返回地址
     *
     * @param bucketName 桶名称
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     * @param file      文件
     *
     */
    public static void storageFile(String bucketName, String key, File file) {
        BufferedInputStream inputStream;
        try {
            inputStream = FileUtil.getInputStream(file);
        } catch (IORuntimeException e) {
            throw new CommonException("获取文件流异常，名称是：{}", file.getName());
        }
        storageFile(bucketName, key, inputStream);
    }

    /**
     * 存储文件，不返回地址
     *
     * @param bucketName 桶名称
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     * @param multipartFile      文件
     *
     */
    public static void storageFile(String bucketName, String key, MultipartFile multipartFile) {
        InputStream inputStream;
        try {
            inputStream = multipartFile.getInputStream();
        } catch (IOException e) {
            throw new CommonException("获取文件流异常，名称是：{}", multipartFile.getName());
        }
        storageFile(bucketName, key, inputStream);
    }

    /**
     * 存储文件流式上传，不返回地址
     *
     * @param bucketName 桶名称
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     * @param bytes      文件字节数组
     *
     */
    public static void storageFile(String bucketName, String key, byte[] bytes) {
        ByteArrayInputStream byteArrayInputStream = null;
        try {
            initClient();
            byteArrayInputStream = new ByteArrayInputStream(bytes);
            obsClient.putObject(bucketName, key,byteArrayInputStream);
        } catch (ObsException  e) {
            throw new CommonException(e.getMessage());
        } finally {
            IoUtil.close(byteArrayInputStream);
        }
    }

    /**
     * 存储文件，不返回地址
     *
     * @param bucketName  桶名称
     * @param key         唯一标示id，例如a.txt, doc/a.txt
     * @param inputStream 文件流
     *
     */
    public static void storageFile(String bucketName, String key, InputStream inputStream) {
        try {
            initClient();
            obsClient.putObject(bucketName, key,inputStream);
        } catch (ObsException e) {
            throw new CommonException(e.getMessage());
        } finally {
            IoUtil.close(inputStream);
        }
    }

    /**
     * 存储文件，返回外网地址
     *
     * @param bucketName 桶名称
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     * @param file      文件
     *
     */
    public static String storageFileWithReturnUrl(String bucketName, String key, File file) {
        storageFile(bucketName, key, file);
        setFileAcl(bucketName, key, DevFileBucketAuthEnum.PUBLIC_READ);
        return getFileAuthUrl(bucketName, key);
    }

    /**
     * 存储文件，返回外网地址
     *
     * @param bucketName 桶名称
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     * @param multipartFile      文件
     *
     */
    public static String storageFileWithReturnUrl(String bucketName, String key, MultipartFile multipartFile) {
        storageFile(bucketName, key, multipartFile);
        setFileAcl(bucketName, key, DevFileBucketAuthEnum.PUBLIC_READ);
        return getFileAuthUrl(bucketName, key);
    }

    /**
     * 存储文件，返回外网地址
     *
     * @param bucketName 桶名称
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     * @param bytes      文件字节数组
     *
     */
    public static String storageFileWithReturnUrl(String bucketName, String key, byte[] bytes) {
        storageFile(bucketName, key, bytes);
        setFileAcl(bucketName, key, DevFileBucketAuthEnum.PUBLIC_READ);
        return getFileAuthUrl(bucketName, key);
    }

    /**
     * 存储文件，返回外网地址
     *
     * @param bucketName  桶名称
     * @param key         唯一标示id，例如a.txt, doc/a.txt
     * @param inputStream 文件流
     *
     */
    public static String storageFileWithReturnUrl(String bucketName, String key, InputStream inputStream) {
        storageFile(bucketName, key, inputStream);
        setFileAcl(bucketName, key, DevFileBucketAuthEnum.PUBLIC_READ);
        return getFileAuthUrl(bucketName, key);
    }

    /**
     * 获取某个bucket下的文件字节
     *
     * @param bucketName 桶名称
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     *
     */
    public static byte[] getFileBytes(String bucketName, String key) {
        InputStream objectContent = null;
        try {
            initClient();
            // 流式下载
            ObsObject obsObject = obsClient.getObject(bucketName, key);
            // 获取文件内容
            objectContent = obsObject.getObjectContent();
            return IoUtil.readBytes(objectContent);
        } catch (ObsException  e) {
            throw new CommonException(e.getMessage());
        } finally {
            IoUtil.close(objectContent);
        }
    }

    /**
     * 设置文件访问权限管理
     *
     * @param bucketName     桶名称
     * @param key            唯一标示id，例如a.txt, doc/a.txt
     * @param devFileBucketAuthEnum 文件权限
     *
     */
    public static void setFileAcl(String bucketName, String key, DevFileBucketAuthEnum devFileBucketAuthEnum) {
        try {
            initClient();
            if (devFileBucketAuthEnum.equals(DevFileBucketAuthEnum.PRIVATE)) {
                obsClient.setObjectAcl(bucketName, key, AccessControlList.REST_CANNED_PRIVATE);
            } else if (devFileBucketAuthEnum.equals(DevFileBucketAuthEnum.PUBLIC_READ)) {
                obsClient.setObjectAcl(bucketName, key, AccessControlList.REST_CANNED_PUBLIC_READ);
            } else if (devFileBucketAuthEnum.equals(DevFileBucketAuthEnum.PUBLIC_READ_WRITE)) {
                obsClient.setObjectAcl(bucketName, key, AccessControlList.REST_CANNED_PUBLIC_READ_WRITE);
            }
        } catch (ObsException e) {
            throw new CommonException(e.getMessage());
        }
    }

    /**
     * 拷贝文件
     *
     * @param originBucketName 源文件桶
     * @param originFileKey    源文件名称
     * @param newBucketName    新文件桶
     * @param newFileKey       新文件名称
     *
     */
    public static void copyFile(String originBucketName, String originFileKey, String newBucketName, String newFileKey) {
        try {
            initClient();
            obsClient.copyObject(originBucketName, originFileKey, newBucketName, newFileKey);
        } catch (ObsException e) {
            throw new CommonException(e.getMessage());
        }
    }

    /**
     * 获取文件的下载地址（带鉴权和有效时间的），生成外网地址
     *
     * @param bucketName 文件桶
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     * @param timeoutMillis 时效
     *
     */
    public static String getFileAuthUrl(String bucketName, String key, Long timeoutMillis) {
        initClient();
        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, timeoutMillis);
        request.setBucketName(bucketName);
        request.setObjectKey(key);
        TemporarySignatureResponse response = obsClient.createTemporarySignature(request);
        return  response.getSignedUrl();
    }

    /**
     * 获取文件的下载地址（永久的，文件必须为公有读），生成外网地址
     *
     * @param bucketName 文件桶
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     *
     */
    public static String getFileAuthUrl(String bucketName, String key) {
        try {
            initClient();
            DevConfigApi devConfigApi = SpringUtil.getBean(DevConfigApi.class);
            String endpoint = devConfigApi.getValueByKey(OHOSCON_FILE_HWYUN_END_POINT_KEY);
            return "https"+ StrUtil.COLON + StrUtil.SLASH + StrUtil.SLASH + bucketName + StrUtil.DOT + endpoint + StrUtil.SLASH + key;
        } catch (ObsException e) {
            throw new CommonException(e.getMessage());
        }
    }

    /**
     * 获取文件的上传地址（带鉴权和有效时间的），生成外网地址
     *
     * @param bucketName 文件桶
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     * @param timeoutMillis 时效
     *
     */
    public static String getFileUploadAuthUrl(String bucketName, String key, Long timeoutMillis) {
        initClient();
        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, timeoutMillis);
        request.setBucketName(bucketName);
        request.setObjectKey(key);
        TemporarySignatureResponse response = obsClient.createTemporarySignature(request);
        return  response.getSignedUrl();
    }



    /**
     * 删除文件
     *
     * @param bucketName 文件桶
     * @param key        唯一标示id，例如a.txt, doc/a.txt
     *
     */
    public static void deleteFile(String bucketName, String key) {
        try{
            initClient();
            obsClient.deleteObject(bucketName, key);
        } catch (ObsException e) {
            throw new CommonException(e.getMessage());
        }
    }

    /**
     * 根据文件名获取ContentType
     *
     *
     **/
    private static String getFileContentType(String key) {
        // 根据文件名获取contentType
        String contentType = "application/octet-stream";
        if (key.contains(".")) {
            contentType = MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);
        }
        return contentType;
    }

    public static String getFileKey(String storagePath){
        //https://openharmonytsc-connect-business.obs.cn-south-1.myhuaweicloud.com/2024/5/13/1789982136414035970.json
        DevConfigApi devConfigApi = SpringUtil.getBean(DevConfigApi.class);
        String endpoint = devConfigApi.getValueByKey(OHOSCON_FILE_HWYUN_END_POINT_KEY);

        storagePath = storagePath.substring(storagePath.lastIndexOf(endpoint+"/"));
        storagePath = storagePath.replace(endpoint+"/","");
        return storagePath.substring(storagePath.indexOf(storagePath));
    }

}
